#ifndef COMMON_H
#define COMMON_H

#include <QPointF>
#include <xkeycheck.h>

class Point {
public:

	constexpr inline Point& operator+=(const Point& p);
	constexpr inline Point& operator-=(const Point& p);
	inline Point& operator-(const Point& p);

	Point() {
		tx = 0;
		ty = 0;
		tz = 0;
	}

	~Point() {
	}

	Point(float x, float y, float z) {
		tx = x;
		ty = y;
		tz = z;
	}

	void setX(float x) {
		tx = x;
	}

	void setY(float y) {
		ty = y;
	}

	void setZ(float z) {
		tz = z;
	}

	float x() {
		return tx;
	}

	float y() {
		return ty;
	}

	float z() {
		return tz;
	}

	Point cross(Point p) {
		return Point(
			ty * p.z() - tz * p.y(),
			tz * p.x() - tx * p.z(),
			tx * p.y() - ty * p.x()
		);
	}
	static Point cross(Point p1, Point p2) {
		return Point(
			p1.y() * p2.z() - p1.z() * p2.y(),
			p1.z() * p2.x() - p1.x() * p2.z(),
			p1.x() * p2.y() - p1.y() * p2.x()
		);
	}

	float dot(Point p) {
		return tx * p.x() + ty * p.y() + tx * p.z();
	}

	static float dot(Point p1, Point p2) {
		return p1.x() * p2.x() + p1.y() * p2.y() + p1.z() * p2.z();
	}

	void from(Point p) {
		tx = p.x();
		ty = p.y();
		tz = p.z();
	}
	void add(Point p) {
		this->tx += p.x();
		this->ty += p.y();
		this->tz += p.z();
	}

	inline float length()
	{
		return std::sqrt(tx * tx + ty * ty + tz * tz);
	}
	inline void normalize()
	{
		const float len = length();
		if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len))
			return;

		tx /= len;
		ty /= len;
		tz /= len;
	}
	inline Point multiply(float data) {
		return Point(tx * data, ty * data, tz * data);
	}

	inline Point divide(float data) {
		if (qFuzzyIsNull(data)) {
			return Point(0, 0, 0);
		}
		return Point(tx / data, ty / data, tz / data);
	}

	inline Point subtract(Point p) {
		return Point(tx - p.x(), ty - p.y(), tz - p.z());
	}
protected:
	float tx;
	float ty;
	float tz;
};

constexpr inline Point& Point::operator+=(const Point& p)
{
	tx += p.tx;
	ty += p.ty;
	tz += p.tz;
	return *this;
}

constexpr inline Point& Point::operator-=(const Point& p)
{
	tx -= p.tx;
	ty -= p.ty;
	tz -= p.tz;
	return *this;
}

inline Point& Point::operator-(const Point& p) {
	Point data = Point(tx - p.tx, ty - p.ty, tz - p.tz);
	return data;
}


class Vector {
public:
	Vector();

	Vector(float x, float y, float z) {
		data[0] = x;
		data[1] = y;
		data[2] = z;
	}
	Point toPoint() {
		return Point(data[0], data[1], data[2]);
	}
	Vector normalized();
	void normalize();
	float length();
	void from(Vector p) {
		data[0] = p.x();
		data[1] = p.y();
		data[2] = p.z();
	}

	float x() {
		return data[0];
	}

	float y() {
		return data[1];
	}

	float z() {
		return data[2];
	}

	void setX(float x) {
		data[0] = x;
	}

	void setY(float y) {
		data[1] = y;
	}

	void setZ(float z) {
		data[2] = z;
	}

	inline Vector multiply(float num) {
		return Vector(data[0] * num, data[1] * num, data[2] * num);
	}

	inline Point divide(float num) {
		if (qFuzzyIsNull(num)) {
			return Point(0, 0, 0);
		}
		return Point(data[0] / num, data[1] / num, data[2] / num);
	}

	inline Vector add(Vector v) {
		return Vector(data[0] + v.x(), data[1] + v.y(), data[2] + v.z());
	}

	inline Vector sub(Vector v) {
		return Vector(data[0] - v.x(), data[1] - v.y(), data[2] - v.z());
	}

	float dot(Vector v) {
		return data[0] * v.x() + data[1] * v.y() + data[2] * v.z();
	}
private:
	float data[3];
};

inline float Vector::length()
{
	using std::hypot;
	return hypot(data[0], data[1], data[2]);
}

inline Vector::Vector()
{
	data[0] = 0;
	data[1] = 0;
	data[2] = 0;
}

inline Vector Vector::normalized()
{
	const float len = length();
	return qFuzzyIsNull(len - 1.0f) ? *this : qFuzzyIsNull(len) ? Vector()
		: Vector(data[0] / len, data[1] / len, data[2] / len);
}

inline void Vector::normalize()
{
	const float len = length();
	if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len))
		return;

	data[0] /= len;
	data[1] /= len;
	data[2] /= len;
}

#endif
